"""
Video timeline management for synchronized presentation generation.

Manages timing, transitions, and synchronization between slides, animations, and audio.
"""

from dataclasses import dataclass
from typing import List, Dict, Optional, Any, Tuple
from enum import Enum

from ..script.script_generator import PresentationScript, ScriptSegment
from ..utils.exceptions import MD2PPTError


class SegmentType(Enum):
    """Types of timeline segments."""
    SLIDE = "slide"
    TRANSITION = "transition"
    AUDIO_ONLY = "audio_only"
    PAUSE = "pause"


@dataclass
class TimelineSegment:
    """A segment in the video timeline."""
    segment_id: str
    segment_type: SegmentType
    start_time: float  # seconds
    duration: float    # seconds
    slide_number: Optional[int] = None
    audio_path: Optional[str] = None
    subtitle_text: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None
    
    @property
    def end_time(self) -> float:
        """Calculate end time of segment."""
        return self.start_time + self.duration
    
    def __str__(self) -> str:
        return f"Segment({self.segment_type.value}, {self.start_time:.1f}s-{self.end_time:.1f}s)"


@dataclass
class VideoConfig:
    """Configuration for video generation."""
    width: int = 1920
    height: int = 1080
    fps: int = 30
    video_codec: str = "libx264"
    audio_codec: str = "aac"
    bitrate: str = "2M"
    audio_bitrate: str = "128k"
    format: str = "mp4"
    
    # Timing settings
    slide_duration: float = 5.0  # Default slide duration if no audio
    transition_duration: float = 0.5  # Transition between slides
    pause_after_slide: float = 0.2  # Brief pause after each slide
    
    # Animation settings
    enable_animations: bool = True
    animation_sync: bool = True  # Sync animations with audio timing
    
    @property
    def aspect_ratio(self) -> float:
        """Calculate aspect ratio."""
        return self.width / self.height
    
    @property
    def resolution_name(self) -> str:
        """Get resolution name."""
        if self.width == 1920 and self.height == 1080:
            return "1080p"
        elif self.width == 1280 and self.height == 720:
            return "720p"
        elif self.width == 3840 and self.height == 2160:
            return "4K"
        elif self.width == 1080 and self.height == 1920:
            return "1080p_vertical"
        else:
            return f"{self.width}x{self.height}"
    
    def to_ffmpeg_args(self) -> List[str]:
        """Convert config to FFmpeg arguments."""
        return [
            "-c:v", self.video_codec,
            "-c:a", self.audio_codec,
            "-b:v", self.bitrate,
            "-b:a", self.audio_bitrate,
            "-r", str(self.fps),
            "-s", f"{self.width}x{self.height}"
        ]


class VideoTimeline:
    """Manages video timeline and synchronization."""
    
    def __init__(self, config: Optional[VideoConfig] = None):
        """Initialize video timeline."""
        self.config = config or VideoConfig()
        self.segments: List[TimelineSegment] = []
        self.total_duration: float = 0.0
        self._audio_files: Dict[int, str] = {}  # slide_number -> audio_path
    
    def create_from_script(self, script: PresentationScript,
                         audio_files: Optional[Dict[int, str]] = None) -> None:
        """
        Create timeline from presentation script and audio files.
        
        Args:
            script: Presentation script with timing information
            audio_files: Optional mapping of slide number to audio file path
        """
        self.segments.clear()
        self._audio_files = audio_files or {}
        
        current_time = 0.0
        
        for segment in script.segments:
            slide_number = segment.slide_number
            
            # Get audio file for this segment
            audio_path = self._audio_files.get(slide_number)
            
            # Determine segment duration
            if audio_path:
                # Use audio duration if available
                segment_duration = segment.duration_estimate
            else:
                # Use default slide duration
                segment_duration = self.config.slide_duration
            
            # Create slide segment
            slide_segment = TimelineSegment(
                segment_id=f"slide_{slide_number}",
                segment_type=SegmentType.SLIDE,
                start_time=current_time,
                duration=segment_duration,
                slide_number=slide_number,
                audio_path=audio_path,
                subtitle_text=segment.content,
                metadata={
                    "title": segment.section_title,
                    "level": segment.section_level
                }
            )
            
            self.segments.append(slide_segment)
            current_time += segment_duration
            
            # Add pause after slide (except for last slide)
            if segment != script.segments[-1] and self.config.pause_after_slide > 0:
                pause_segment = TimelineSegment(
                    segment_id=f"pause_after_{slide_number}",
                    segment_type=SegmentType.PAUSE,
                    start_time=current_time,
                    duration=self.config.pause_after_slide
                )
                
                self.segments.append(pause_segment)
                current_time += self.config.pause_after_slide
            
            # Add transition (except for last slide)
            if segment != script.segments[-1] and self.config.transition_duration > 0:
                transition_segment = TimelineSegment(
                    segment_id=f"transition_{slide_number}_to_{slide_number + 1}",
                    segment_type=SegmentType.TRANSITION,
                    start_time=current_time,
                    duration=self.config.transition_duration,
                    metadata={
                        "from_slide": slide_number,
                        "to_slide": slide_number + 1,
                        "transition_type": "fade"
                    }
                )
                
                self.segments.append(transition_segment)
                current_time += self.config.transition_duration
        
        self.total_duration = current_time
    
    def add_segment(self, segment: TimelineSegment) -> None:
        """Add a segment to the timeline."""
        self.segments.append(segment)
        self.total_duration = max(self.total_duration, segment.end_time)
    
    def get_segments_at_time(self, time: float) -> List[TimelineSegment]:
        """Get all segments active at a specific time."""
        active_segments = []
        
        for segment in self.segments:
            if segment.start_time <= time <= segment.end_time:
                active_segments.append(segment)
        
        return active_segments
    
    def get_slide_segments(self) -> List[TimelineSegment]:
        """Get all slide segments."""
        return [s for s in self.segments if s.segment_type == SegmentType.SLIDE]
    
    def get_transition_segments(self) -> List[TimelineSegment]:
        """Get all transition segments."""
        return [s for s in self.segments if s.segment_type == SegmentType.TRANSITION]
    
    def get_audio_segments(self) -> List[TimelineSegment]:
        """Get all segments with audio."""
        return [s for s in self.segments if s.audio_path]
    
    def optimize_timeline(self) -> None:
        """Optimize timeline by removing unnecessary segments and adjusting timing."""
        # Remove zero-duration segments
        self.segments = [s for s in self.segments if s.duration > 0]
        
        # Sort segments by start time
        self.segments.sort(key=lambda x: x.start_time)
        
        # Merge adjacent pause segments
        merged_segments = []
        current_pause = None
        
        for segment in self.segments:
            if segment.segment_type == SegmentType.PAUSE:
                if current_pause is None:
                    current_pause = segment
                else:
                    # Extend current pause
                    current_pause.duration += segment.duration
            else:
                if current_pause:
                    merged_segments.append(current_pause)
                    current_pause = None
                merged_segments.append(segment)
        
        # Don't forget the last pause
        if current_pause:
            merged_segments.append(current_pause)
        
        self.segments = merged_segments
        
        # Recalculate total duration
        if self.segments:
            self.total_duration = max(s.end_time for s in self.segments)
        else:
            self.total_duration = 0.0
    
    def export_timeline_data(self) -> Dict[str, Any]:
        """Export timeline data for external tools."""
        return {
            "config": {
                "width": self.config.width,
                "height": self.config.height,
                "fps": self.config.fps,
                "duration": self.total_duration
            },
            "segments": [
                {
                    "id": seg.segment_id,
                    "type": seg.segment_type.value,
                    "start_time": seg.start_time,
                    "duration": seg.duration,
                    "end_time": seg.end_time,
                    "slide_number": seg.slide_number,
                    "audio_path": seg.audio_path,
                    "subtitle_text": seg.subtitle_text,
                    "metadata": seg.metadata or {}
                }
                for seg in self.segments
            ]
        }
    
    def get_keyframes(self, interval: float = 1.0) -> List[float]:
        """
        Get keyframe times for video encoding optimization.
        
        Args:
            interval: Minimum interval between keyframes
            
        Returns:
            List of keyframe times in seconds
        """
        keyframes = [0.0]  # Always start with a keyframe
        
        for segment in self.segments:
            # Add keyframe at segment start
            if segment.start_time - keyframes[-1] >= interval:
                keyframes.append(segment.start_time)
            
            # Add keyframe at significant transitions
            if segment.segment_type == SegmentType.TRANSITION:
                mid_time = segment.start_time + segment.duration / 2
                if mid_time - keyframes[-1] >= interval:
                    keyframes.append(mid_time)
        
        # Add final keyframe
        if self.total_duration - keyframes[-1] >= interval:
            keyframes.append(self.total_duration)
        
        return sorted(set(keyframes))
    
    def calculate_progress_at_time(self, time: float) -> float:
        """Calculate progress percentage at given time."""
        if self.total_duration == 0:
            return 0.0
        return min(100.0, (time / self.total_duration) * 100.0)
    
    def get_timeline_summary(self) -> str:
        """Get a human-readable timeline summary."""
        slide_count = len(self.get_slide_segments())
        transition_count = len(self.get_transition_segments())
        audio_count = len(self.get_audio_segments())
        
        summary = [
            f"视频时长: {self.total_duration:.1f} 秒",
            f"分辨率: {self.config.resolution_name} ({self.config.width}x{self.config.height})",
            f"帧率: {self.config.fps} FPS",
            f"幻灯片: {slide_count} 页",
            f"转场: {transition_count} 个",
            f"音频: {audio_count} 个文件"
        ]
        
        return "\n".join(summary)


class TimelineError(MD2PPTError):
    """Raised when timeline operations fail."""
    pass